package com.heima.movement.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.models.common.Result;
import com.heima.models.movement.pojos.Likes;
import com.heima.models.movement.pojos.Movement;
import com.heima.models.movement.pojos.MovementComment;
import com.heima.movement.mapper.LikesMapper;
import com.heima.movement.mapper.MovementCommentMapper;
import com.heima.movement.mapper.MovementMapper;
import com.heima.movement.service.LikesService;
import com.heima.movement.utils.ThreadUtil;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

@Service
public class LikesServiceImpl extends ServiceImpl<LikesMapper, Likes> implements LikesService {

    @Autowired
    private LikesMapper likesMapper;

    @Autowired
    private MovementMapper movementMapper;

    @Autowired
    private MovementCommentMapper movementCommentMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public Result like(String targetId, Integer type) {
        // 1. 入参判空
        if (StrUtil.isBlank(targetId)) {
            return Result.error("入参不能为空");
        }

        // 2. 用线程去执行MySQL的写入操作
        // pool(targetId, type);

        // addToDb(targetId, type);

        // 使用rabbitMQ进行消息的发送
        // 方法一：
        // 发送一条数据，消费的时候，执行MySQL两张表的操作 + Redis的操作
        // 方法二：
        // 插入点赞表，更新动态表，更新redis（分为三条消息去发）
        // 参数一：指定接收消息的队列
        // 参数二：要发送的消息 -> JSON (targetId, type)
        Map<String, String> map = new HashMap<>();
        map.put("targetId", targetId);
        map.put("type", type.toString());
        rabbitTemplate.convertAndSend("queue.like", JSON.toJSONString(map));

        return Result.success("");
    }

    /**
     * 方案一：使用@Async + @EnableSync注解实现多线程
     */
    @Async
    public void addToDb(String targetId, Integer type) {
        // 2. like记录表加一条记录
        Likes likes = new Likes();
        likes.setTargetId(targetId);
        likes.setType(type);
        likes.setUserId(0);
        likes.setCreatedTime(LocalDateTime.now());
        int insertResult = likesMapper.insert(likes);
        if (insertResult != 1) {
            return;
        }

        // 3. 动态表点赞数+1
        // 根据type去判断，如果是1，就更新动态，如果是2，就更新评论
        if (type == 1) {
            Movement movement = movementMapper.selectById(targetId);
            if (movement != null) {
                Integer likesCount = movement.getLikes();
                movement.setLikes(likesCount + 1);
                int updateResult = movementMapper.updateById(movement);
                if (updateResult != 1) {
                    // 需要触发前面一个MySQl写入操作的回滚
                    throw new RuntimeException("点赞更新失败");
                }
            }

            // 4. 更新Redis里面的排名
            // 参数一：集合名
            // 参数二：数据
            // 参数三：累加的分值
            redisTemplate.opsForZSet().incrementScore("movement_list", JSON.toJSONString(movement), 3);
        } else {
            MovementComment movementComment = movementCommentMapper.selectById(targetId);
            if (movementComment != null) {
                Integer likesCount = movementComment.getLikes();
                movementComment.setLikes(likesCount + 1);
                int commentResult = movementCommentMapper.updateById(movementComment);
                if (commentResult != 1) {
                    throw new RuntimeException("评论点赞数更新失败");
                }
            }
        }
    }

    /**
     * 方案二：使用线程池实现多线程
     * 需要配合 ThreadUtils类 + LikeThread线程类才能工作
     */
    public void pool(String targetId, Integer type) {
        ThreadPoolExecutor executor = ThreadUtil.getInstance();
        LikeThread likeThread = new LikeThread(movementMapper, movementCommentMapper, likesMapper, targetId, type);
        executor.execute(likeThread);
    }

    /**
     * 方案三：使用RabbitMQ进行异步写入
     * 需要实现 消息发送 + 消息接收两部分
     * 该方法是消息接收
     */
    @RabbitListener(queues = "queue.like")
    public void addToDbFromMQ(String message) {
        Map map = JSON.parseObject(message, Map.class);
        String targetId = (String) map.get("targetId");
        String type = (String) map.get("type");

        // 2. like记录表加一条记录
        Likes likes = new Likes();
        likes.setTargetId(targetId);
        likes.setType(Integer.parseInt(type));
        likes.setUserId(0);
        likes.setCreatedTime(LocalDateTime.now());
        int insertResult = likesMapper.insert(likes);
        if (insertResult != 1) {
            return;
        }

        // 3. 动态表点赞数+1
        // 根据type去判断，如果是1，就更新动态，如果是2，就更新评论
        if ("1".equals(type)) {
            Movement movement = movementMapper.selectById(targetId);
            if (movement != null) {
                Integer likesCount = movement.getLikes();
                movement.setLikes(likesCount + 1);
                int updateResult = movementMapper.updateById(movement);
                if (updateResult != 1) {
                    // 需要触发前面一个MySQl写入操作的回滚
                    throw new RuntimeException("点赞更新失败");
                }
            }

            // 4. 更新Redis里面的排名
            // 参数一：集合名
            // 参数二：数据
            // 参数三：累加的分值
            redisTemplate.opsForZSet().incrementScore("movement_list", JSON.toJSONString(movement), 3);
        } else {
            MovementComment movementComment = movementCommentMapper.selectById(targetId);
            if (movementComment != null) {
                Integer likesCount = movementComment.getLikes();
                movementComment.setLikes(likesCount + 1);
                int commentResult = movementCommentMapper.updateById(movementComment);
                if (commentResult != 1) {
                    throw new RuntimeException("评论点赞数更新失败");
                }
            }
        }

        System.out.println("消费了一条信息");
    }

}
